/**
 * Name         Cell Zooming in Green Cellular Networks
 * @File        Channel.cpp
 * Authors      Bruno Saldanha Carminati & Marcelo Faleiros Costa
 * Date         May 2012
 *
 * This file is part of the undergraduate final project, 
 * under the supervision of André Noll Barreto
 */

#include <cstdlib>
#include <iostream>
#include <list>
#include <math.h>
#include <vector>
#include "BaseStation.h"
#include "Channel.h"
#include "MobileUser.h"

using namespace std;

/**
 * This class does not have any constructor
 */

/**
 * Calculates distance between base station and mobile user
 */
double Channel::length(BaseStation baseStation, MobileUser user) {
    
    return sqrt(pow((baseStation.xPos-user.xPos),2)+pow((baseStation.yPos-user.yPos),2));
}

/**
 * Calculates power loss in dB due to large scale fading
 * Extended Cost 231 Okumura-Hata's method (for frequencies between 1.5 and 2.0 GHz)
 */
double Channel::largeScaleFading(BaseStation baseStation, MobileUser user) {
    double frequencyFactor = 46.3+33.9*log10(1.70);
    double antennaHeightFactor = -13.82*log10(baseStation.zPos);
    double correctionFactor = -3.2*(pow((log10(11.75*user.zPos)),2));
    double distanceFactor = (44.9-6.55*log10(baseStation.zPos))*log10(this->length(baseStation,user));
    double aditionalFactor = 0;
    
    return frequencyFactor+antennaHeightFactor+correctionFactor+distanceFactor+aditionalFactor;
}

/**
 * Calculates power loss in dB due to shadow fading
 * Linear Interpolation
 */
double Channel::shadowFading(BaseStation baseStation, MobileUser user) {
    double decorrelationDistance = 50;
    
    /* Calculates mobile user position inside a square of base station unique grid */
    double xPosInsideSquare = 0;
    double yPosInsideSquare = 0;
    
    if (user.xPos-baseStation.xPos < 0) {
        xPosInsideSquare = decorrelationDistance+(user.xPos-baseStation.xPos-decorrelationDistance*(int)((user.xPos-baseStation.xPos)/decorrelationDistance));
    }
    else {
        xPosInsideSquare = user.xPos-baseStation.xPos-decorrelationDistance*(int)((user.xPos-baseStation.xPos)/decorrelationDistance);
    }
    
    if (user.yPos-baseStation.yPos < 0) {
        yPosInsideSquare = decorrelationDistance+(user.yPos-baseStation.yPos-decorrelationDistance*(int)((user.yPos-baseStation.yPos)/decorrelationDistance));
    }
    else {
        yPosInsideSquare = user.yPos-baseStation.yPos-decorrelationDistance*(int)((user.yPos-baseStation.yPos)/decorrelationDistance);
    }    
    
    /* Verifies in which quadrant of the base station unique grid is the mobile user */
    int whichQuadrant = 0;
    
    if (user.xPos >= baseStation.xPos) {
        if (user.yPos >= baseStation.yPos) {
            whichQuadrant = 0; /* Mobile user is on Quadrant I */
        }
        else {
            whichQuadrant = 3; /* Mobile user is on Quadrant IV */
        }
    }
    else {
        if (user.yPos >= baseStation.yPos) {
            whichQuadrant = 1; /* Mobile user is on Quadrant II */
        }
        else {
            whichQuadrant = 2; /* Mobile user is on Quadrant III */
        }
    }
    
    /* Retrieves shadowing factor from matrix for each node of the square */
    double shadowingFactorOnNode3 = baseStation.shadowingFactorMatrix[whichQuadrant][(int)((user.xPos-baseStation.xPos)/decorrelationDistance)][(int)((user.yPos-baseStation.yPos)/decorrelationDistance)];
    double shadowingFactorOnNode2 = baseStation.shadowingFactorMatrix[whichQuadrant][(int)((user.xPos-baseStation.xPos)/decorrelationDistance)+1][(int)((user.yPos-baseStation.yPos)/decorrelationDistance)];
    double shadowingFactorOnNode1 = baseStation.shadowingFactorMatrix[whichQuadrant][(int)((user.xPos-baseStation.xPos)/decorrelationDistance)+1][(int)((user.yPos-baseStation.yPos)/decorrelationDistance)+1];
    double shadowingFactorOnNode0 = baseStation.shadowingFactorMatrix[whichQuadrant][(int)((user.xPos-baseStation.xPos)/decorrelationDistance)][(int)((user.yPos-baseStation.yPos)/decorrelationDistance)+1];
    
    /* Interpolates shadowing factor inside square */
    double firstMember = shadowingFactorOnNode0*(sqrt(yPosInsideSquare/decorrelationDistance))+shadowingFactorOnNode3*(sqrt(1-yPosInsideSquare/decorrelationDistance));
    double secondMember = shadowingFactorOnNode1*sqrt(yPosInsideSquare/decorrelationDistance)+shadowingFactorOnNode2*sqrt(1-yPosInsideSquare/decorrelationDistance);
    double shadowingFactor = (sqrt(1-xPosInsideSquare/decorrelationDistance))*(firstMember)+(sqrt(xPosInsideSquare/decorrelationDistance))*(secondMember);

    return shadowingFactor;
}

/**
 *
 *
 */
void Channel::link(std::vector<BaseStation>& baseStationVector, std::list<MobileUser>& mobileUserList) {
    
    list<MobileUser>::iterator mobileUserListIterator;
    vector<BaseStation>::iterator baseStationVectorIterator;
    int rowsCounter = 0, colsCounter = 0;
    int baseStationChosen = 0, numberOfUsers = 0;
    
    for (rowsCounter = 0; rowsCounter < mobileUserList.size(); rowsCounter++) {
        for (colsCounter = 0; colsCounter < baseStationVector.size(); colsCounter++) {
            this->linkMatrix[rowsCounter][colsCounter] = 0;
        }
    }
    
    for (mobileUserListIterator = mobileUserList.begin(), rowsCounter = 0; mobileUserListIterator != mobileUserList.end(); mobileUserListIterator++, rowsCounter++) {
        double minFading = 9999999;
        for(baseStationVectorIterator = baseStationVector.begin(), colsCounter = 0; baseStationVectorIterator != baseStationVector.end(); baseStationVectorIterator++, colsCounter++) {
            double pathLoss=this->largeScaleFading(*baseStationVectorIterator,*mobileUserListIterator)+this->shadowFading(*baseStationVectorIterator,*mobileUserListIterator);
            if (pathLoss <= minFading) {
                minFading = pathLoss;
                baseStationChosen = colsCounter;
            }
            this->pathLossMatrix[rowsCounter][colsCounter] = pathLoss;
//            cout << "BTS \t" << baseStationVectorIterator->xPos << "\t" << baseStationVectorIterator->yPos << "\t" << baseStationVectorIterator->zPos << endl;
//            cout << "\t" << pathLoss << "\t+\t " << sf << " dB" << endl;
        }
        this->linkMatrix[rowsCounter][baseStationChosen] = 1;
//        cout << "\nMobile User " << rowsCounter << " is linked to Base Station " << baseStationChosen << endl;
    }
    
    for (colsCounter = 0; colsCounter < baseStationVector.size(); colsCounter++) {
        numberOfUsers = 0;
        for (rowsCounter = 0; rowsCounter < mobileUserList.size(); rowsCounter++) {
            if (this->linkMatrix[rowsCounter][colsCounter] == 1) {
                numberOfUsers++;
            }
        }
        cout << "BTS " << colsCounter << "\thas " << numberOfUsers << " MU's" << endl;
    }
}

